package com.itheima.easy.handler.aliyun;

import cn.hutool.core.date.DateUtil;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.alipay.easysdk.kernel.Config;
import com.alipay.easysdk.kernel.util.ResponseChecker;
import com.alipay.easysdk.payment.common.models.*;
import com.baomidou.mybatisplus.core.incrementer.IdentifierGenerator;
import com.itheima.easy.client.alipay.Factory;
import com.itheima.easy.config.AliPayConfig;
import com.itheima.easy.constant.SuperConstant;
import com.itheima.easy.constant.TradingConstant;
import com.itheima.easy.enums.TradingEnum;
import com.itheima.easy.exception.ProjectException;
import com.itheima.easy.handler.BeforePayHandler;
import com.itheima.easy.handler.CommonPayHandler;
import com.itheima.easy.mq.MqMessage;
import com.itheima.easy.pojo.RefundRecord;
import com.itheima.easy.pojo.Trading;
import com.itheima.easy.service.IRefundRecordService;
import com.itheima.easy.service.ITradingService;
import com.itheima.easy.utils.BeanConv;
import com.itheima.easy.utils.EmptyUtil;
import com.itheima.easy.utils.ExceptionsUtil;
import com.itheima.easy.vo.RefundRecordVo;
import com.itheima.easy.vo.TradingVo;
import lombok.extern.slf4j.Slf4j;
import org.springframework.amqp.rabbit.core.RabbitTemplate;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import java.sql.Timestamp;
import java.time.LocalDateTime;

/**
 * @ClassName AliCommonPayHandler.java
 * @Description 阿里支付基础支付
 */
@Slf4j
@Service
public class AliCommonPayHandler implements CommonPayHandler {

    @Autowired
    AliPayConfig aliPayConfig;

    @Autowired
    ITradingService tradingService;

    @Autowired
    IRefundRecordService refundRecordService;

    @Autowired
    IdentifierGenerator identifierGenerator;

    @Autowired
    BeforePayHandler beforePayHandler;

    @Autowired
    RabbitTemplate rabbitTemplate;

    @Override
    public TradingVo queryTrading(TradingVo tradingVo) {
        //1、查询前置处理：检测交易单参数
        Boolean flag = beforePayHandler.checkQueryTrading(tradingVo);
        if (!flag){
            throw new ProjectException(TradingEnum.CHECK_TRADING_FAIL);
        }
        Trading tradingHandler = tradingService.findTradByTradingOrderNo(tradingVo.getTradingOrderNo());
        //2、获得支付宝配置文件
        Config config = aliPayConfig.config(tradingVo.getEnterpriseId());
        //3、配置如果为空，抛出异常
        if (EmptyUtil.isNullOrEmpty(config)){
            throw new ProjectException(TradingEnum.CONFIG_ERROR);
        }
        //4、使用配置
        Factory factory = new Factory();
        factory.setOptions(config);
        try {
            //5、调用支付宝API：通用查询支付情况
            AlipayTradeQueryResponse queryResponse = factory.Common()
                .query(String.valueOf(tradingVo.getTradingOrderNo()));
            //6、判断响应是否成功
            boolean success = ResponseChecker.success(queryResponse);
            //7、响应成功，分析交易状态
            if (success&&!EmptyUtil.isNullOrEmpty(queryResponse.getTradeStatus())){
                switch (queryResponse.getTradeStatus()){
                    //支付取消：TRADE_CLOSED（未付款交易超时关闭，或支付完成后全额退款）
                    case TradingConstant.ALI_TRADE_CLOSED:
                        tradingHandler.setTradingState(TradingConstant.TRADE_CLOSED);break;
                    //支付成功：TRADE_SUCCESS（交易支付成功）
                    case TradingConstant.ALI_TRADE_SUCCESS:
                        tradingHandler.setTradingState(TradingConstant.TRADE_SUCCESS);break;
                    //支付成功：TRADE_FINISHED（交易结束，不可退款）
                    case TradingConstant.ALI_TRADE_FINISHED:
                        tradingHandler.setTradingState(TradingConstant.TRADE_SUCCESS);break;
                    //非最终状态不处理，当前交易状态：WAIT_BUYER_PAY（交易创建，等待买家付款）不处理
                    default:
                        flag = false;break;
                }
                //8.1、修改交易单状态
                if (flag){
                    tradingHandler.setResultCode(queryResponse.getSubCode());
                    tradingHandler.setResultMsg(queryResponse.getSubMsg());
                    tradingHandler.setResultJson(JSONObject.toJSONString(queryResponse));
                    tradingService.updateById(tradingHandler);

                    //8.2 发送消息到MQ
                    Long messageId = (Long) identifierGenerator.nextId(tradingHandler);
                    MqMessage mqMessage = MqMessage.builder()
                        .id(messageId)
                        .title("trade-message")
                        .content(JSONObject.toJSONString(tradingHandler))
                        .messageType("trade-project-sync")
                        .produceTime(Timestamp.valueOf(LocalDateTime.now()))
                        .sender("system")
                        .build();

                    /**
                     * trade-exchange : 交换机的名称
                     * enterpriseId ： 企业id，对应的值为渠道表中的enterprise_id字段的值。
                     *                 每个业务系统都有自己的队列，通过路由key将消息路由到自己的队列
                     *                 此处的路由key就是enterpriseId的值
                     */
                    rabbitTemplate.convertAndSend("trade-exchange",tradingVo.getEnterpriseId().toString(), JSON.toJSONString(mqMessage));

                    return BeanConv.toBean(tradingHandler,TradingVo.class);
                }else {
                    log.info("查询支付宝交易单：{},结果：{}", tradingVo.getTradingOrderNo(),queryResponse.getTradeStatus());
                }
            }else {
                throw new RuntimeException("网关：查询支付宝统一下单失败！");
            }
        } catch (Exception e) {
            log.warn("查询支付宝统一下单失败：{}", ExceptionsUtil.getStackTraceAsString(e));
            throw new RuntimeException("查询支付宝统一下单失败！");
        }
        //7、返回结果
        return tradingVo;
    }

    @Override
    public TradingVo refundTrading(TradingVo tradingVo) {
        //1、生成退款请求编号
        tradingVo.setOutRequestNo(String.valueOf(identifierGenerator.nextId(tradingVo)));
        Trading tradingHandler = tradingService.findTradByTradingOrderNo(tradingVo.getTradingOrderNo());
        tradingVo.setTradingAmount(tradingHandler.getTradingAmount());
        tradingVo.setRefund(tradingHandler.getRefund());
        //2、退款前置处理：检测交易单参数
        Boolean flag = beforePayHandler.checkRefundTrading(tradingVo);
        if (!flag){
            throw new ProjectException(TradingEnum.CHECK_TRADING_FAIL);
        }
        //3、退款前置处理：退款幂等性校验
        beforePayHandler.idempotentRefundTrading(tradingVo);
        //4、获得支付宝配置文件
        Config config = aliPayConfig.config(tradingVo.getEnterpriseId());
        //5、配置如果为空，抛出异常
        if (EmptyUtil.isNullOrEmpty(config)){
            throw new ProjectException(TradingEnum.CONFIG_ERROR);
        }
        //6、使用配置
        Factory factory = new Factory();
        factory.setOptions(config);
        //7、调用退款接口
        try {
            AlipayTradeRefundResponse refundResponse = factory.Common()
                .optional("out_request_no", tradingVo.getOutRequestNo())
                .refund(String.valueOf(tradingVo.getTradingOrderNo()),
                        String.valueOf(tradingVo.getOperTionRefund()));
            //8、判断响应是否成功
            boolean success = ResponseChecker.success(refundResponse);
            if (success&&String.valueOf(tradingVo.getTradingOrderNo()).equals(refundResponse.getOutTradeNo())){
                //9、保存交易单信息
                tradingHandler.setIsRefund(SuperConstant.YES);
                tradingHandler.setRefund(tradingHandler.getRefund().add(tradingVo.getOperTionRefund()));
                tradingService.updateById(tradingHandler);
                //10、保存退款单信息
                RefundRecord refundRecord = RefundRecord.builder()
                    .enterpriseId(tradingHandler.getEnterpriseId())
                    .storeId(tradingHandler.getStoreId())
                    .refundNo(tradingVo.getOutRequestNo())
                    .refundAmount(tradingVo.getOperTionRefund())
                    .refundCode(refundResponse.getSubCode())
                    .refundMsg(refundResponse.getSubMsg())
                    .productOrderNo(tradingHandler.getProductOrderNo())
                    .tradingChannel(tradingHandler.getTradingChannel())
                    .tradingOrderNo(tradingHandler.getTradingOrderNo())
                    .refundStatus(success?TradingConstant.REFUND_STATUS_SENDING:TradingConstant.REFUND_STATUS_FAIL)
                    .memo("退款："+tradingHandler.getMemo())
                    .build();
                refundRecordService.save(refundRecord);
            }else {
                log.error("网关：支付宝统一下单退款：{},结果：{}", tradingVo.getTradingOrderNo(),
                    JSONObject.toJSONString(refundResponse));
                throw new RuntimeException("网关：支付宝统一下单退款失败");
            }
        } catch (Exception e) {
            log.error("支付宝统一下单退款失败：{}", ExceptionsUtil.getStackTraceAsString(e));
            throw new ProjectException(TradingEnum.TRAD_REFUND_FAIL);
        }
        return BeanConv.toBean(tradingHandler,TradingVo.class);
    }

    @Override
    public RefundRecordVo queryRefundTrading(RefundRecordVo refundRecordVo) {
        //1、退款前置处理：检测退款单参数
        Boolean flag = beforePayHandler.checkQueryRefundTrading(refundRecordVo);
        if (!flag){
            throw new ProjectException(TradingEnum.CHECK_TRADING_FAIL);
        }
        RefundRecord refundRecordHandler = refundRecordService.findRefundRecordByRefundNo(refundRecordVo.getRefundNo());
        //2、获得支付宝配置文件
        Config config = aliPayConfig.config(refundRecordVo.getEnterpriseId());
        //3、配置如果为空，抛出异常
        if (EmptyUtil.isNullOrEmpty(config)){
            throw new ProjectException(TradingEnum.CONFIG_ERROR);
        }
        //4、使用配置
        Factory factory = new Factory();
        factory.setOptions(config);
        try {
            AlipayTradeFastpayRefundQueryResponse refundQueryResponse =
                factory.Common().queryRefund(
                String.valueOf(refundRecordVo.getTradingOrderNo()),
                refundRecordVo.getRefundNo());
            //5、判断响应是否成功
            boolean success = ResponseChecker.success(refundQueryResponse);
            //6、查询出的退款状态
            String refundStatus = refundQueryResponse.getRefundStatus();
            if (success&&TradingConstant.REFUND_SUCCESS.equals(refundStatus)){
                refundRecordHandler.setRefundStatus(TradingConstant.REFUND_STATUS_SUCCESS);
                refundRecordHandler.setRefundCode(refundQueryResponse.getSubCode());
                refundRecordHandler.setRefundMsg(refundQueryResponse.getSubMsg());
                refundRecordService.updateById(refundRecordHandler);
            }else {
                log.error("网关：查询支付宝统一下单退款：{},结果：{}", refundRecordVo.getTradingOrderNo(),
                    JSONObject.toJSONString(refundQueryResponse));
                throw new RuntimeException("网关：查询支付宝统一下单退款失败！");
            }
        } catch (Exception e) {
            log.warn("查询支付宝统一下单退款失败：{}", ExceptionsUtil.getStackTraceAsString(e));
            throw new RuntimeException("查询支付宝统一下单退款失败！");
        }
        return refundRecordVo;
    }

    @Override
    public TradingVo closeTrading(TradingVo tradingVo) {
        //1、退款前置处理：检测关闭参数tradingVo
        Boolean flag = beforePayHandler.checkCloseTrading(tradingVo);
        if (!flag){
            throw new ProjectException(TradingEnum.CHECK_TRADING_FAIL);
        }
        Trading tradingHandler = tradingService.findTradByTradingOrderNo(tradingVo.getTradingOrderNo());
        //2、获得支付宝配置文件
        Config config = aliPayConfig.config(tradingVo.getEnterpriseId());
        //3、配置如果为空，抛出异常
        if (EmptyUtil.isNullOrEmpty(config)){
            throw new ProjectException(TradingEnum.CONFIG_ERROR);
        }
        //4、使用配置
        Factory factory = new Factory();
        factory.setOptions(config);
        try {
            //5、调用三方API关闭订单
            AlipayTradeCloseResponse closeResponse = factory.Common().close(
                String.valueOf(tradingVo.getTradingOrderNo()));
            //6、关闭订单受理情况
            boolean success = ResponseChecker.success(closeResponse);
            if (success&&tradingVo.getTradingOrderNo().equals(closeResponse.getOutTradeNo())){
                tradingHandler.setTradingState(TradingConstant.TRADE_CLOSED);
                tradingService.updateById(tradingHandler);
                return BeanConv.toBean(tradingHandler,TradingVo.class);
            }else {
                log.error("网关：支付宝关闭订单：{},结果：{}", tradingVo.getTradingOrderNo(),
                        JSONObject.toJSONString(closeResponse));
                throw  new RuntimeException("网关：支付宝关闭订单失败!");
            }
        } catch (Exception e) {
            log.warn("关闭订单失败：{}", ExceptionsUtil.getStackTraceAsString(e));
            throw  new ProjectException(TradingEnum.TRAD_CLOSE_FAIL);
        }
    }

    @Override
    public TradingVo downLoadBill(TradingVo tradingVo) {
        //1、下载账单前置处理：检测关闭参数tradingVo
        Boolean flag = beforePayHandler.checkDownLoadBill(tradingVo);
        if (!flag){
            throw new ProjectException(TradingEnum.CHECK_TRADING_FAIL);
        }
        //2、获得支付宝配置文件
        Config config = aliPayConfig.config(tradingVo.getEnterpriseId());
        //3、配置如果为空，抛出异常
        if (EmptyUtil.isNullOrEmpty(config)){
            throw new ProjectException(TradingEnum.CONFIG_ERROR);
        }
        //4、使用配置
        Factory factory = new Factory();
        factory.setOptions(config);
        try {
            AlipayDataDataserviceBillDownloadurlQueryResponse billDownloadurlQueryResponse = factory.Common()
                .downloadBill(tradingVo.getBillType(), DateUtil.formatDate(tradingVo.getBillDate()));
            //5、请求下载受理情况
            boolean success = ResponseChecker.success(billDownloadurlQueryResponse);
            if (success){
                String billDownloadUrl = billDownloadurlQueryResponse.getBillDownloadUrl();
                tradingVo.setBillDownloadUrl(billDownloadUrl);
            }else {
                log.warn("网关：支付宝下载订单类型：{},时间：{},结果：{}", tradingVo.getBillType(),tradingVo.getBillDate(),
                        JSONObject.toJSONString(billDownloadurlQueryResponse));
                throw new RuntimeException("网关：支付宝下载订单失败！");
            }
        } catch (Exception e) {
            log.warn("支付宝下载订单失败：{}", ExceptionsUtil.getStackTraceAsString(e));
            throw  new RuntimeException("支付宝下载订单失败！");
        }
        return tradingVo;
    }

}
